import java.util.Date;
import java.util.concurrent.CopyOnWriteArrayList;

import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PFont;

public class TimeLine {
	private PApplet parent = null;
	private Tooltip tooltip = null;

	private long startTime = 0;
	private int maxTime = 0;
	private int[] numberOfIssues = null;
	
	// TimeLine Fonts
	private PFont fontSmallDate = null;
	private PFont fontCurrentDate = null;
	
	// Text Coordinates and Colors
	private int CURRENTDATEX = 0;
	private int CURRENTDATEY = 0;
	private int CURRENTDATESTROKE = 0;
	private int CURRENTDATEFILL = 0;
	private int SMALLDATESTROKE = 0;
	private int SMALLDATEFILL = 0;

	// BG Rect Coordinates and Colors
	private int BGX1 = 0;
	private int BGY1 = 0;
	private int BGX2 = 0;
	private int BGY2 = 0;
	private int BGSTROKE = 0;
	private int BGFILL = 0;

	private boolean showBackground = false;

	// Backdrop Rect Coordinates and Colors
	private int BACKDROPX1 = 0;
	private int BACKDROPY1 = 0;
	private int BACKDROPX2 = 0;
	private int BACKDROPY2 = 0;
	private int BACKDROPSTROKE = 0;
	private int BACKDROPFILL = 0;

	// Bar Size and Color
	private int centerValue = 0;
	
	private int CENTERX1 = 0;
	private int CENTERY1 = 0;
	private int CENTERX2 = 0;
	private int CENTERY2 = 0;
	private int CENTERSTROKE = 0;
	private int CENTERFILL = 0;
	private int BARSTROKE = 0;
	private int BARFILL = 0;
	private int BARXSPACING = 0; // Only adjust this here
	private double BARYSPACING = 0;
		
	public TimeLine(PApplet p, Tooltip t) {
		this.parent = p;
		this.tooltip = t;
		this.fontSmallDate = parent.createFont("FFScala", 12);
		this.fontCurrentDate = parent.createFont("FFScala", 12);
		setup();
	}
	
	public void setup() {
		// Modify These
		BGSTROKE = parent.color(0,0,255,125);
		BGFILL = parent.color(0,0,255,125);
		BACKDROPSTROKE = parent.color(255,220,220);
		BACKDROPFILL = parent.color(255,200,200);
		CENTERSTROKE = parent.color(255,0,0,130);
		CENTERFILL = parent.color(255,0,0,130);
		BARSTROKE = parent.color(255,255,255);
		BARFILL = parent.color(0,0,0);
		CURRENTDATESTROKE = parent.color(255,255,255);
		CURRENTDATEFILL = parent.color(255,255,255);
		SMALLDATESTROKE = parent.color(255,255,255);
		SMALLDATEFILL = parent.color(255,255,255);
		
		BARXSPACING = 4;
	}
	
	public void update(int x1, int y1, int x2, int y2, boolean show) {
		// Don't Modify These
		BGX1 = x1;
		BGY1 = y1;
		BGX2 = x2;
		BGY2 = y2;
		showBackground = show;
		
		BACKDROPX1 = BGX1;
		BACKDROPY1 = BGY1+fontSmallDate.theight;
		BACKDROPX2 = BGX2;
		BACKDROPY2 = BGY2-fontSmallDate.theight;
		
		CURRENTDATEX = 5;
		CURRENTDATEY = BACKDROPY1-2;		

		CENTERX1 = ((BGX2-BGX1)/2)-(BARXSPACING/2);
		CENTERY1 = BACKDROPY1-3;
		CENTERX2 = ((BGX2-BGX1)/2)+(BARXSPACING/2);
		CENTERY2 = BACKDROPY2+3;

		if(this.numberOfIssues == null || this.numberOfIssues.length == 0)
			BARYSPACING = 0;
		else
			BARYSPACING = (double)((BACKDROPY2-BACKDROPY1))/getMaxIssues();
	}

	public void loadIssues(CopyOnWriteArrayList<Issue> colI, long start, int max) {
		this.startTime = start;
		this.maxTime = max;
		this.numberOfIssues = new int[this.maxTime];
		for(int i = 0; i < colI.size(); i++)
			for(int j = colI.get(i).getOpenDate()-1; j < (colI.get(i).getCloseDate() == 0 ? this.maxTime-1 : colI.get(i).getCloseDate()-1); j++)
				this.numberOfIssues[j]++;
	}
	
	public boolean isOver() {
		if(BGX1 <= parent.mouseX && parent.mouseX <= BGX2 && BGY1 <= parent.mouseY && parent.mouseY <= BGY2)
			return true;
		
		return false;
	}
	
	private int getMaxIssues() {
		int curMax = 1;
		for(int i = 0; i < this.numberOfIssues.length; i++)
			if(this.numberOfIssues[i] > curMax)
				curMax = this.numberOfIssues[i];
		
		return curMax;
	}
	
	private int calculateIssues(double time, int timeInc) {
		int num = 0;
		int range = (timeInc % 2 == 0 ? (timeInc/2)-1 : (timeInc/2));
		for(double i = -range; i <= range; i++)
			num += this.numberOfIssues[(int)(time+i)];
		num /= (range*2)+1;
		return num;
	}
	
	@SuppressWarnings("deprecation")
	private String formatDate(double days, double timeInc) {
		if(this.startTime == 0)
			return "";

		Date date = new Date(startTime + (long)(days*24*60*60*1000));
		String[] months = {"Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"};
		if(timeInc <= 5)
			return (months[date.getMonth()] + " " + date.getDate() + ", " + (date.getYear()+1900));
		else if(timeInc <= 60)
			return (months[date.getMonth()] + ", " + (date.getYear()+1900));
		else
			return new Integer(date.getYear()+1900).toString();
	}
	
	@SuppressWarnings("deprecation")
	private String formatSmallDate(double days, int timeInc) {
		if(this.startTime == 0)
			return "";

		Date date = new Date(startTime + (long)(days*24*60*60*1000));
		String[] months = {"Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"};
		if(date.getDate() == 1)
			if(date.getMonth() == 0)
				return new Integer(date.getYear()+1900).toString();
			else if(date.getMonth() % timeInc == 0)
				return months[date.getMonth()];

			return "";
	}

	public void display(double curTime, int timeInc) {
		parent.rectMode(PConstants.CORNERS);
		
		// Draw Background
		if(showBackground) {
			parent.stroke(BGSTROKE);
			parent.fill(BGFILL);
			parent.strokeWeight(1);
			parent.rect(BGX1, BGY1, BGX2, BGY2);
		}

		// Draw Backdrop
		parent.stroke(BACKDROPSTROKE);
		parent.fill(BACKDROPFILL);
		parent.strokeWeight(1);
		parent.rect(BACKDROPX1, BACKDROPY1, BACKDROPX2, BACKDROPY2);

		// Draw Current Date
		parent.stroke(CURRENTDATESTROKE);
		parent.fill(CURRENTDATEFILL);
		parent.strokeWeight(1);
		parent.textFont(fontCurrentDate);
		parent.textAlign(PConstants.LEFT);
		parent.text(formatDate(curTime,timeInc), CURRENTDATEX, CURRENTDATEY);
				
		// Draw Bottom Dates
		parent.textFont(fontSmallDate);
		parent.textAlign(PConstants.CENTER, PConstants.TOP);
		parent.stroke(SMALLDATESTROKE);
		parent.fill(SMALLDATEFILL);
		for(double i = BACKDROPX1; i < BACKDROPX2; i += 1.0/timeInc)
			if(formatSmallDate(curTime+((i-CENTERX1)*timeInc/BARXSPACING), timeInc).length() != 0) {
				parent.text(formatSmallDate(curTime+((i-CENTERX1)*timeInc/BARXSPACING), timeInc), (int)i, BACKDROPY2);
				parent.line((int)i, BACKDROPY1, (int)i, BACKDROPY2);
				i += BARXSPACING;
			}

		// Draw Bars
		parent.strokeWeight(1);
		for(int i = (BACKDROPX1-CENTERX1)/BARXSPACING; i < (BACKDROPX2-CENTERX2)/BARXSPACING; i++)
			if(curTime+(i*timeInc)+Math.ceil(timeInc/2) < this.maxTime && curTime+(i*timeInc)-Math.ceil(timeInc/2) >= 0) {
				int x1 = CENTERX1+(i*BARXSPACING);
				int y1 = (int)(BACKDROPY2-(BARYSPACING*calculateIssues(curTime+(i*timeInc), timeInc)));
				int x2 = CENTERX2+(i*BARXSPACING);
				int y2 = BACKDROPY2;
				
				parent.stroke(BARSTROKE);
				parent.fill(BARFILL);
				
				parent.rect(x1, y1, x2, y2);
				// Set Tooltip
				if(CENTERX1 <= x1 && x2 <= CENTERX2 && y1 >= CENTERY1 && y2 <= CENTERY2)
					centerValue = calculateIssues(curTime+(i*timeInc), timeInc);

				if(x1 <= parent.mouseX && parent.mouseX <= x2 && parent.mouseY >= y1 && parent.mouseY <= y2)
					tooltip.setValue(calculateIssues(curTime+(i*timeInc), timeInc));
			}		
		
		// Draw Center
		parent.stroke(CENTERSTROKE);
		parent.fill(CENTERFILL);
		parent.strokeWeight(1);
		parent.rect(CENTERX1, CENTERY1, CENTERX2, CENTERY2);
		if(CENTERX1 <= parent.mouseX && parent.mouseX <= CENTERX2 && parent.mouseY >= CENTERY1 && parent.mouseY <= CENTERY2)
			tooltip.setValue(centerValue);				
	}
}